package ascii;

/**
 * ASCII Character used to display BBS Art.
 * @author Guan-Zhong Huang
 */
public abstract class AsciiChar {

    /**
     * Return simularity between the grid and character. The size of grid is
     * 8x8.
     * @param pixel  2d array of pixels, in which stores color indices
     * @param gridX  upper left X position of grid in 2d array
     * @param gridY  upper left Y position of grid in 2d array
     * @param fgColor_i  color index of the foreground color
     * @param bgColor_i  color index of the background color
     * @return simularity value
     */
    public abstract float matchGrid(byte[][] pixel, int gridX, int gridY,
            int fgColor_i, int bgColor_i);

    /**
     * Return Ansi code consisting of color code and character.
     */
    public abstract String getAnsiCode(int fgColor_i, int bgColor_i);

    protected String getAnsiCode(char ch, int fgColor_i, int bgColor_i) {
        StringBuilder builder = new StringBuilder("\033[");
        builder.append(AnsiColor.fgColorCode(fgColor_i));
        builder.append(';');
        builder.append(AnsiColor.bgColorCode(bgColor_i));
        builder.append('m');
        builder.append(ch);
        builder.append("\033[m");
        return builder.toString();
    }

    protected String getAnsiCode(char ch, int fgColor_i) {
        StringBuilder builder = new StringBuilder("\033[");
        builder.append(AnsiColor.fgColorCode(fgColor_i));
        builder.append('m');
        builder.append(ch);
        builder.append("\033[m");
        return builder.toString();
    }
}

class BlockChar {

    static float matchLeftBlock(byte[][] pixel, int gridX, int gridY,
            int fgColor_i, int bgColor_i, int fgW) {
        int fgCount = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = 0; j < fgW; ++j) {
                if (pixel[gridY + i][gridX + j] == fgColor_i) {
                    ++fgCount;
                }
            }
        }
        int bgCount = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = fgW; j < 8; ++j) {
                if (pixel[gridY + i][gridX + j] == bgColor_i) {
                    ++bgCount;
                }
            }
        }
        float sim = 0.f;
        sim += fgCount / (float) (8 * fgW);
        if (fgW < 8)
            sim += bgCount / (float) (8 * (8 - fgW));
        if (((bgColor_i >> 3) & 0x1) == 0x1)
            return sim;
        else
            return 2.0f * sim;
    }

    static float matchLowerBlock(byte[][] pixel, int gridX, int gridY,
            int fgColor_i, int bgColor_i, int fgH) {
        int bgCount = 0;
        for (int i = 0; i < 8 - fgH; ++i) {
            for (int j = 0; j < 8; ++j) {
                if (pixel[gridY + i][gridX + j] == bgColor_i) {
                    ++bgCount;
                }
            }
        }
        int fgCount = 0;
        for (int i = 8 - fgH; i < 8; ++i) {
            for (int j = 0; j < 8; ++j) {
                if (pixel[gridY + i][gridX + j] == fgColor_i) {
                    ++fgCount;
                }
            }
        }
        float sim = 0.f;
        if (fgH < 8)
            sim += bgCount / (float) (8 * (8 - fgH));
        sim += fgCount / (float) (8 * fgH);
        if (((bgColor_i >> 3) & 0x1) == 0x1) {
            return sim;
        } else
            return 2.0f * sim;
    }
}

class LowerOneEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▁';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 1);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerOneQuarterBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▂';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 2);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerThreeEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▃';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 3);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerHalfBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▄';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 4);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerFiveEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▅';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 5);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerThreeQuarterBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▆';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 6);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerSevenEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▇';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 7);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class FullBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '█';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLowerBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 8);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i);
    }
}

class LeftSevenEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▉';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 7);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftThreeQuarterBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▊';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 6);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftFiveEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▋';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 5);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftHalfBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▌';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 4);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftThreeEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▍';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 3);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftOneQuarterBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▎';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 2);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LeftOneEighthBlock extends AsciiChar {

    static private char ch;

    static {
        ch = '▏';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        return BlockChar.matchLeftBlock(pixel, gridX, gridY, fgColor_i,
                bgColor_i, 1);
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class TriangleChar {

    static int countLowerRight(byte[][] pixel, int gridX, int gridY,
            int color_i) {
        int count = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = 7 - i; j < 8; ++j) {
                if (pixel[gridY + i][gridX + j] == color_i) {
                    ++count;
                }
            }
        }
        return count;
    }

    static int countUpperLeft(byte[][] pixel, int gridX, int gridY,
            int color_i) {
        int count = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = 0; j < 8 - i; ++j) {
                if (pixel[gridY + i][gridX + j] == color_i) {
                    ++count;
                }
            }
        }
        return count;
    }

    static int countLowerLeft(byte[][] pixel, int gridX, int gridY,
            int color_i) {
        int count = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = 0; j <= i; ++j) {
                if (pixel[gridY + i][gridX + j] == color_i) {
                    ++count;
                }
            }
        }
        return count;
    }

    static int countUpperRight(byte[][] pixel, int gridX, int gridY,
            int color_i) {
        int count = 0;
        for (int i = 0; i < 8; ++i) {
            for (int j = i; j < 8; ++j) {
                if (pixel[gridY + i][gridX + j] == color_i) {
                    ++count;
                }
            }
        }
        return count;
    }
}

class LowerRightTriangle extends AsciiChar {

    static private char ch;

    static {
        ch = '◢';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        int bgCount =
                TriangleChar.countUpperLeft(pixel, gridX, gridY, bgColor_i);
        int fgCount =
                TriangleChar.countLowerRight(pixel, gridX, gridY, fgColor_i);
        float sim = (bgCount + fgCount) / 32.f;
        if (((bgColor_i >> 3) & 0x1) == 0x1)
            return sim;
        else
            return 2.0f * sim;
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class LowerLeftTriangle extends AsciiChar {

    static private char ch;

    static {
        ch = '◣';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        int bgCount =
                TriangleChar.countUpperRight(pixel, gridX, gridY, bgColor_i);
        int fgCount =
                TriangleChar.countLowerLeft(pixel, gridX, gridY, fgColor_i);
        float sim = (bgCount + fgCount) / 32.f;
        if (((bgColor_i >> 3) & 0x1) == 0x1)
            return sim;
        else
            return 2.0f * sim;
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class UpperLeftTriangle extends AsciiChar {

    static private char ch;

    static {
        ch = '◤';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        int bgCount =
                TriangleChar.countLowerRight(pixel, gridX, gridY, bgColor_i);
        int fgCount =
                TriangleChar.countUpperLeft(pixel, gridX, gridY, fgColor_i);
        float sim = (bgCount + fgCount) / 32.f;
        if (((bgColor_i >> 3) & 0x1) == 0x1)
            return sim;
        else
            return 2.0f * sim;
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}

class UpperRightTriangle extends AsciiChar {

    static private char ch;

    static {
        ch = '◥';
    }

    @Override
    public float matchGrid(byte[][] pixel, int gridX, int gridY, int fgColor_i,
            int bgColor_i) {
        int bgCount =
                TriangleChar.countUpperRight(pixel, gridX, gridY, bgColor_i);
        int fgCount =
                TriangleChar.countLowerLeft(pixel, gridX, gridY, fgColor_i);
        float sim = (bgCount + fgCount) / 32.f;
        if (((bgColor_i >> 3) & 0x1) == 0x1)
            return sim;
        else
            return 2.0f * sim;
    }

    @Override
    public String getAnsiCode(int fgColor_i, int bgColor_i) {
        return getAnsiCode(ch, fgColor_i, bgColor_i);
    }
}
